// Copyright (C) 2014 by The Hourglass-Resurrection Team.
// Hourglass-Resurrection is licensed under GPL v2.
// Full notice is in COPYING.txt

#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>
#include <tchar.h>
#include <strsafe.h>
#include "Resource.h"
//#include <stdio.h>
#include <vector>
#include <algorithm>

HINSTANCE hSavedInstance;
BOOL bUnsavedChanges = FALSE;
std::vector<std::vector<LPWSTR>*> vecResourceH;

#define IDDigitCount(id) ((id > 9999) ? 5 : (id > 999) ? 4 : 3)

// These are sorted in the order which they are to be assigned IDs.
//	Accelelerator	start at 0x64, prio 1
//	Icon			continue, prio 2
//	String			continue, prio 3
//	Dialog			continue, prio 4
//	Control			start at 0x1000
//	Menu			start at 0x8000
// Since there is no written standard for how statically allocated data inside a binary is to be ordered by a compiler/linker
// this string is allocated all at once. And then the pointers for the other variables are assigned using this allocation and an offset.
// It's done like this to speed up sorting compares, instead of finding the type for each parameter and returning a bool based on that,
// all you need to do is type1 < type2, and sorting compare is done.
// Much faster and more effective than a highly complicated chain of wcscmp checks.
// -- Warepire
static const LPWSTR wstrAllTypes = L"ACCEL\0ICON\0STR\0DLG\0CTRL\0MENU\0 ";
static const LPWSTR wstrAccel = wstrAllTypes;
static const LPWSTR wstrIcon = wstrAccel + 6; // Make sure to include the NULL byte with the offset
static const LPWSTR wstrString = wstrIcon + 5;
static const LPWSTR wstrDialog = wstrString + 4;
static const LPWSTR wstrControl = wstrDialog + 4;
static const LPWSTR wstrMenu = wstrControl + 5;
static const LPWSTR wstrNothing = wstrMenu + 5;

int CALLBACK ListViewSortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	LPWSTR wstrParam1 = ((std::vector<LPWSTR>*)lParam1)->at(2);
	LPWSTR wstrParam2 = ((std::vector<LPWSTR>*)lParam2)->at(2);
	int iParam1 = wcstol(wstrParam1, NULL, 10);
	int iParam2 = wcstol(wstrParam2, NULL, 10);
	return iParam1 - iParam2; // if iParam1 is smaller than iParam2 the result is negative, otherwise it's positive, or 0 if equal.
}

int CALLBACK ListViewSortByTypeAndName(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	std::vector<LPWSTR>* vecParam1 = (std::vector<LPWSTR>*)lParam1;
	std::vector<LPWSTR>* vecParam2 = (std::vector<LPWSTR>*)lParam2;
	bool bSameType = (vecParam1->at(0)) == (vecParam2->at(0));
	if(bSameType)
	{
		return wcscmp(vecParam1->at(1), vecParam2->at(1)); // If param 1 is "smaller", it goes first.
	}
	else // If they are not the same type, there is no need to check further as 
	{
		// Read comment at the top of the file where the Type-strings are initialized to see how this
		// is a guaranteed correct and safe way to compare the strings.
		return (int)((vecParam1->at(0)) - (vecParam2->at(0)));
	}
}

void AutoAdjustListViewColumns(HWND hListView)
{
	int iColumnCount = Header_GetItemCount(ListView_GetHeader(hListView));
	for(int i = 0; i < iColumnCount; i++)
	{
		ListView_SetColumnWidth(hListView, i, LVSCW_AUTOSIZE);
	}
}

void ClearVecResourceH()
{
	while(vecResourceH.empty() == false)
	{
		size_t szSize = vecResourceH.back()->size();
		while(szSize > 1) // First string is hard-coded
		{
			delete [] vecResourceH.back()->back();
			vecResourceH.back()->pop_back();
			szSize = vecResourceH.back()->size();
		}
		vecResourceH.back()->pop_back();
		delete vecResourceH.back();
		vecResourceH.pop_back();
	}
}

BOOL TestResourceHNameCollision(const LPWSTR wstrType, const LPWSTR wstrName)
{
	DWORD dwItems = vecResourceH.size();
	for(DWORD dw = 0; dw < dwItems; dw++)
	{
		if((wstrType == vecResourceH[dw]->at(0)) && (wcscmp(wstrName, vecResourceH[dw]->at(1)) == 0))
			return TRUE; // Collision
	}
	return FALSE; // No collision
}
BOOL TestResourceHIDCollision(const LPWSTR wstrID)
{
	DWORD dwItems = vecResourceH.size();
	for(DWORD dw = 0; dw < dwItems; dw++)
	{
		if(wcscmp(wstrID, vecResourceH[dw]->at(2)) == 0)
			return TRUE; // Collision
	}
	return FALSE; // No collision
}

BOOL ParseResourceHToListView(HWND hListView, HANDLE hResourceH)
{
	DWORD dwFileSize = GetFileSize(hResourceH, NULL);
	DWORD dwReadBytes;
	PBYTE wstrResourceH = new BYTE[dwFileSize+1]; // It's a LPWSTR, but we need to allocate it as bytes due to how we read it.
	ReadFile(hResourceH, wstrResourceH, dwFileSize, &dwReadBytes, NULL);
	if(dwFileSize != dwReadBytes) goto ParseResourceHToListView_Failed;

	LVITEM lvItem;

	static const LPWSTR wstrEndMarker = L"#ifdef";	
	PWCHAR lpEndMarker = wcsstr((PWCHAR)wstrResourceH, wstrEndMarker);
	PWCHAR lpCurrentPosition = wcsstr((PWCHAR)wstrResourceH, L"#define");
	PWCHAR lpItemEnd = NULL;
	DWORD dwStrLen;
	int iCurrentRow = 0;
	int iCurrentItem = 0;
	while(lpCurrentPosition < lpEndMarker && lpCurrentPosition != NULL)
	{
		while(iswspace(*lpCurrentPosition) == 0) lpCurrentPosition++; // Spool past the #define.
		while(iswspace(*lpCurrentPosition) != 0) lpCurrentPosition++; // Spool past the white spaces.
		lpItemEnd = lpCurrentPosition;
		while(iswspace(*lpItemEnd) == 0) lpItemEnd++; // Spool past the item name.

		// Don't allow editing this one. In fact, don't even load it.
		// The check has to be done here unforunately as it is not a requirement that this one is at the very bottom of the file,
		// meaning it cannot be detected and blindly used as the end-marker.
		if(wcsncmp(lpCurrentPosition, L"IDC_STATIC", 10) == 0)
		{
			lpCurrentPosition = wcsstr(lpCurrentPosition, L"#define");
			continue;
		}

		vecResourceH.push_back(new std::vector<LPWSTR>());

		if(wcsncmp(lpCurrentPosition, L"IDR", 3) == 0)      vecResourceH.back()->push_back(wstrAccel);
		else if(wcsncmp(lpCurrentPosition, L"IDI", 3) == 0) vecResourceH.back()->push_back(wstrIcon);
		else if(wcsncmp(lpCurrentPosition, L"IDS", 3) == 0) vecResourceH.back()->push_back(wstrString);
		else if(wcsncmp(lpCurrentPosition, L"IDD", 3) == 0) vecResourceH.back()->push_back(wstrDialog);
		else if(wcsncmp(lpCurrentPosition, L"IDC", 3) == 0) vecResourceH.back()->push_back(wstrControl);
		else if(wcsncmp(lpCurrentPosition, L"IDM", 3) == 0) vecResourceH.back()->push_back(wstrMenu);
		else /* No Category-ID Marker*/                     vecResourceH.back()->push_back(wstrNothing);

		lvItem.mask = LVIF_TEXT | LVIF_PARAM;
		lvItem.iItem = iCurrentRow++;
		lvItem.iSubItem = iCurrentItem++;
		lvItem.lParam = (LPARAM)(vecResourceH.back()); // We only get one chance to set this for the entire row. Use the entire vector.
		lvItem.pszText = vecResourceH.back()->back();
		int iRowIndex = ListView_InsertItem(hListView, &lvItem);

		PWCHAR tmp = wcschr(lpCurrentPosition, L'_');
		if(tmp < lpEndMarker && tmp != NULL)
		{
			lpCurrentPosition = ++tmp;
		}
		dwStrLen = (lpItemEnd - lpCurrentPosition);
		vecResourceH.back()->push_back(new WCHAR[dwStrLen+1]);

		wcsncpy(vecResourceH.back()->back(), lpCurrentPosition, dwStrLen);
		vecResourceH.back()->back()[dwStrLen] = L'\0'; // Terminate string.

		ListView_SetItemText(hListView, iRowIndex, iCurrentItem++, vecResourceH.back()->back());

		lpCurrentPosition = lpItemEnd;
		while(iswspace(*lpCurrentPosition) != 0) lpCurrentPosition++; // Spool past the white spaces.
		lpItemEnd = lpCurrentPosition;
		while(iswspace(*lpItemEnd) == 0) lpItemEnd++; // Spool past the item name.
		dwStrLen = (lpItemEnd - lpCurrentPosition);

		vecResourceH.back()->push_back(new WCHAR[dwStrLen+1]);

		wcsncpy(vecResourceH.back()->back(), lpCurrentPosition, dwStrLen);
		vecResourceH.back()->back()[dwStrLen] = L'\0'; // Terminate string.

		ListView_SetItemText(hListView, iRowIndex, iCurrentItem++, vecResourceH.back()->back());

		lpCurrentPosition = wcsstr(lpCurrentPosition, L"#define");
		iCurrentItem = 0;
	}

	AutoAdjustListViewColumns(hListView);

	delete [] wstrResourceH;
	return TRUE;
ParseResourceHToListView_Failed:
	if(wstrResourceH != NULL)
	{
		delete [] wstrResourceH;
	}
	return FALSE;
}

INT_PTR CALLBACK EditDialogFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static std::vector<LPWSTR>* vecItemInfo = NULL;
	static BOOL bEditing = FALSE;
	static BOOL bEditsMade = FALSE;
	WORD wEventID;
	WORD wEvent;

	switch(uMsg)
	{
		case WM_INITDIALOG:
		{
			// Populate the ComboBox.
			HWND hComboBox = GetDlgItem(hWnd, IDC_EDIT_TYPES);
			ComboBox_AddString(hComboBox, wstrAccel);
			ComboBox_AddString(hComboBox, wstrIcon);
			ComboBox_AddString(hComboBox, wstrString);
			ComboBox_AddString(hComboBox, wstrDialog);
			ComboBox_AddString(hComboBox, wstrControl);
			ComboBox_AddString(hComboBox, wstrMenu);
			// Also set the item data so we can access these values by their original pointer.
			// If this was an owner-drawn ComboBox this would become set automatically,
			// but that would require us to supply text height etc for the drawing... meh.
			// This is simpler.
			ComboBox_SetItemData(hComboBox, 0, (LPARAM)wstrAccel);
			ComboBox_SetItemData(hComboBox, 1, (LPARAM)wstrIcon);
			ComboBox_SetItemData(hComboBox, 2, (LPARAM)wstrString);
			ComboBox_SetItemData(hComboBox, 3, (LPARAM)wstrDialog);
			ComboBox_SetItemData(hComboBox, 4, (LPARAM)wstrControl);
			ComboBox_SetItemData(hComboBox, 5, (LPARAM)wstrMenu);
			// Save the pointer to the vector we're editing.
			if(lParam != NULL)
			{
				SetWindowText(hWnd, L"Edit Item");
				vecItemInfo = ((std::vector<LPWSTR>*)lParam);
				// Load the info into the edit prompt.
				int iComboBoxItem = ComboBox_FindStringExact(hComboBox, -1, vecItemInfo->at(0));
				if(iComboBoxItem != CB_ERR) ComboBox_SetCurSel(hComboBox, iComboBoxItem);
				Edit_SetText(GetDlgItem(hWnd, IDC_EDIT_NAME), vecItemInfo->at(1));
				Edit_SetText(GetDlgItem(hWnd, IDC_EDIT_ID), vecItemInfo->at(2));
				bEditing = TRUE;
			}
			else
			{
				SetWindowText(hWnd, L"New Item");
				bEditing = FALSE;
			}
			break;
		}
		case WM_COMMAND:
		{
			wEventID = LOWORD(wParam);
			wEvent = HIWORD(wParam);
			switch(wEventID)
			{
				case IDC_EDIT_OK:
				{
					// Create markers:
					BOOL bReplaceType = FALSE;
					BOOL bReplaceName = FALSE;
					BOOL bReplaceID = FALSE;
					// Validate the input:
					int iComboBoxSel = ComboBox_GetCurSel(GetDlgItem(hWnd, IDC_EDIT_TYPES));
					DWORD dwNameStrLen = Edit_GetTextLength(GetDlgItem(hWnd, IDC_EDIT_NAME)) + 1;
					DWORD dwIDStrLen = Edit_GetTextLength(GetDlgItem(hWnd, IDC_EDIT_ID)) + 1; 
					if(iComboBoxSel == -1 || dwNameStrLen == 1 || dwIDStrLen == 1)
					{
						MessageBox(hWnd, L"One or more fields are left blank.", L"Error!", MB_OK);
						break; // Guard against empty input.
					}
					int iTypeRes = 0x7FFFFFFF; // Signed int max
					int iNameRes = 0x7FFFFFFF; // Signed int max
					int iIDRes = 0x7FFFFFFF; // Signed int max
					LPWSTR wstrTempType = (LPWSTR)ComboBox_GetItemData(GetDlgItem(hWnd, IDC_EDIT_TYPES), iComboBoxSel);
					LPWSTR wstrTempName = new WCHAR[dwNameStrLen];
					LPWSTR wstrTempID = new WCHAR[dwIDStrLen];
					Edit_GetText(GetDlgItem(hWnd, IDC_EDIT_NAME), wstrTempName, dwNameStrLen);
					Edit_GetText(GetDlgItem(hWnd, IDC_EDIT_ID), wstrTempID, dwIDStrLen);
					if(vecItemInfo != NULL) // If we're editing, check if we made any changes at all
					{
						iTypeRes = wstrTempType - vecItemInfo->at(0);
						iNameRes = wcscmp(wstrTempName, vecItemInfo->at(1));
						iIDRes = wcscmp(wstrTempID, vecItemInfo->at(2));
						if((iTypeRes == 0) && (iNameRes == 0) && (iIDRes == 0)) // Input never changed... just break out of this.
						{
							SendMessage(hWnd, WM_CLOSE, NULL, NULL);
							break;
						}
					}
					// Save changes into the vector, don't write them to file.
					do { // fake loop
						if(iTypeRes != 0 || iNameRes != 0) // New Name.
						{
							if(TestResourceHNameCollision(wstrTempType, wstrTempName) == FALSE)
							{	
								if(iTypeRes != 0) bReplaceType = TRUE;
								if(iNameRes != 0) bReplaceName = TRUE;
							}
							else
							{
								MessageBox(hWnd, L"This type and name is already in use!", L"Error!", MB_OK);
								break;
							}
						}
						if(iIDRes != 0) // New ID.
						{
							if(TestResourceHIDCollision(wstrTempID) == FALSE)
							{
								// Do value checks:
								DWORD dwID = wcstol(wstrTempID, NULL, 10);
								if(wstrTempType == wstrMenu)
								{
									if(dwID < 0x8000)
									{
										MessageBox(hWnd, L"Menu ID's may not be lower than 32768!", L"Error!", MB_OK);
										break;
									}
								}
								else if(wstrTempType == wstrControl)
								{
									if(dwID < 0x1000)
									{
										MessageBox(hWnd, L"Control ID's may not be lower than 4096!", L"Error!", MB_OK);
										break;
									}
									else if(dwID > 0x6FFF)
									{
										MessageBox(hWnd, L"Control ID's may not be larger than 28671!", L"Error!", MB_OK);
										break;
									}
								}
								else
								{
									if(dwID < 0x64)
									{
										MessageBox(hWnd, L"This type's ID may not be lower than 100!", L"Error!", MB_OK);
										break;
									}
									else if(dwID >= 0x0FFF)
									{
										MessageBox(hWnd, L"This type's ID's may not be larger than 4095!", L"Error!", MB_OK);
										break;
									}
								}

								bReplaceID = TRUE;
							}
							else
							{
								MessageBox(hWnd, L"This ID is already in use!", L"Error!", MB_OK);
								break;
							}
						}

						// Don't create this until now, or we might end up with a lost instance of this vector floating in memory.
						if(bEditing == FALSE)
						{
							vecItemInfo = new std::vector<LPWSTR>;
							vecItemInfo->resize(3, NULL); // Make sure it can hold the pointers we'll add to it.
						}

						if(bReplaceType)
						{
							vecItemInfo->at(0) = wstrTempType;
							bEditsMade = TRUE;
						}
						if(bReplaceName)
						{
							if(bEditing) delete [] vecItemInfo->at(1);
							vecItemInfo->at(1) = wstrTempName;
							bEditsMade = TRUE;
						}
						if(bReplaceID)
						{
							if(bEditing) delete [] vecItemInfo->at(2);
							vecItemInfo->at(2) = wstrTempID;
							bEditsMade = TRUE;
						}
						if(bEditing == FALSE)
						{
							vecResourceH.push_back(vecItemInfo);
						}

					} while(false);

					// If we didn't edit name or ID, we need to clean up.
					// Do not delete wstrTempType as that is a pointer to a static string in the binary!
					// Trying to delete it will cause the program to crash.
					if(bEditsMade == FALSE || bReplaceName == FALSE)	delete [] wstrTempName;
					if(bEditsMade == FALSE || bReplaceID == FALSE)		delete [] wstrTempID;

					SendMessage(hWnd, WM_CLOSE, NULL, NULL);
					break;
				}
				case IDC_EDIT_CANCEL:
				{
					SendMessage(hWnd, WM_CLOSE, NULL, NULL);
					break;
				}
				default: return FALSE;
			}
			break;
		}
		case WM_CLOSE:
		{
			// Clean up.
			BOOL bRetValue = bEditsMade;
			bEditsMade = FALSE;
			vecItemInfo = NULL;
			// Return from dialog.
			EndDialog(hWnd, (INT_PTR)bRetValue);
			break;
		}
		default: return FALSE;
	}
	
	return TRUE;
}

INT_PTR CALLBACK MainDialogFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	WORD wEventID;
	WORD wEvent;

	switch(uMsg)
	{
		case WM_INITDIALOG:
		{
			// Set up listview columns.
			HWND hwndListView = GetDlgItem(hWnd, IDC_MAIN_VIEW);
			LVCOLUMN lvcColData;
			lvcColData.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
			lvcColData.fmt = LVCFMT_LEFT;
			lvcColData.cx = 40;
			lvcColData.pszText = L"Type";
			ListView_InsertColumn(hwndListView, 0, &lvcColData);
			lvcColData.pszText = L"Name";
			ListView_InsertColumn(hwndListView, 1, &lvcColData);
			lvcColData.pszText = L"ID";
			ListView_InsertColumn(hwndListView, 2, &lvcColData);
			lvcColData.pszText = L"Used";
			ListView_InsertColumn(hwndListView, 3, &lvcColData);

			// Set the rule for the listview to hightlight entire rows on row-click
			ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_FULLROWSELECT);

			break;
		}
		case WM_COMMAND:
		{
			wEventID = LOWORD(wParam);
			wEvent = HIWORD(wParam);
			switch(wEventID)
			{
				case IDC_MAIN_OPEN: // TODO: This shall be locked to Hourglass-Resurrection Resource.h file
				{
					WCHAR wstrFilename[MAX_PATH+1] = { L'\0' };
					WCHAR wstrCurrentDirectory[MAX_PATH+1] = { L'\0' };
					GetCurrentDirectory(MAX_PATH, wstrCurrentDirectory);
					OPENFILENAME ofn = { sizeof(OPENFILENAME) };
					ofn.hwndOwner = hWnd;
					ofn.hInstance = hSavedInstance;
					ofn.lpstrFilter = L"Hourglass-Resurrection Resrouce file (*.h)\0*.h\0\0";
					ofn.nFilterIndex = 1;
					ofn.lpstrFile = wstrFilename;
					ofn.nMaxFile = MAX_PATH;
					ofn.lpstrInitialDir = wstrCurrentDirectory;
					ofn.lpstrTitle = L"Open Resource.h";
					ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
					ofn.lpstrDefExt = L"h";
					if(GetOpenFileName(&ofn) == FALSE)
					{
						MessageBox(hWnd, L"The file failed to open!", L"Error!", MB_OK);
						break;
					}
					HANDLE hFile = CreateFile(wstrFilename, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					if(hFile == INVALID_HANDLE_VALUE ||
						ParseResourceHToListView(GetDlgItem(hWnd, IDC_MAIN_VIEW), hFile) == FALSE) // Test order execution abuse
					{
						MessageBox(hWnd, L"The file failed to open!", L"Error!", MB_OK);
						break;
					}
					break;
				}
				case IDC_MAIN_NEW: // Merge with Edit
				{
					if(vecResourceH.empty())
					{
						break;
					}

					HWND hListView = GetDlgItem(hWnd, IDC_MAIN_VIEW);
					INT_PTR iEditControlRes = DialogBoxParam(hSavedInstance, MAKEINTRESOURCE(IDD_EDITITEM), hWnd, EditDialogFunc, NULL);
					if(iEditControlRes == -1)
					{
						MessageBox(hWnd, L"Failed to open the Edit-control.", L"Error!", MB_OK);
						break;
					}
					if(iEditControlRes == TRUE) // Changes were made, update the listview so that it reflects the change
					{
						// Add the new row
						int iCurrentItem = 0;
						LVITEM lvItem;
						lvItem.iItem = ListView_GetItemCount(hListView); // 0-based index, no motification necessary
						lvItem.iSubItem = iCurrentItem++;
						lvItem.mask = LVIF_TEXT | LVIF_PARAM;
						lvItem.pszText = vecResourceH.back()->at(0);
						lvItem.lParam = (LPARAM)vecResourceH.back();
						int iItem = ListView_InsertItem(hListView, &lvItem); // Use the iItem value anyway, JUST to be EXTRA safe.
						ListView_SetItemText(hListView, iItem, iCurrentItem++, vecResourceH.back()->at(1));
						ListView_SetItemText(hListView, iItem, iCurrentItem, vecResourceH.back()->at(2));
						// Update the display
						ListView_SortItems(hListView, ListViewSortFunc, NULL);
						AutoAdjustListViewColumns(hListView);
						// Mark that there are unsaved changes
						bUnsavedChanges = TRUE;
					}
					break;
				}
				case IDC_MAIN_EDIT:
				{
					if(vecResourceH.empty()) // Don't try to do this on an empty vector.
					{
						break;
					}

					// Find the selected item. ListView is terrible for not having a GetSelectedItems...
					int iItem = 0;
					HWND hListView = GetDlgItem(hWnd, IDC_MAIN_VIEW);
					if(ListView_GetSelectedCount(hListView) == 0) break; // No item selected, no item to edit.
					for(; (ListView_GetItemState(hListView, iItem, LVIS_SELECTED) == 0); iItem++);
					// Get the info from the selected item.
					LVITEM lvItem;
					lvItem.iItem = iItem;
					lvItem.mask = LVIF_PARAM;
					ListView_GetItem(hListView, &lvItem);
					// Launch the edit-control
					INT_PTR iEditControlRes = DialogBoxParam(hSavedInstance, MAKEINTRESOURCE(IDD_EDITITEM), hWnd, EditDialogFunc, lvItem.lParam);
					if(iEditControlRes == -1)
					{
						MessageBox(hWnd, L"Failed to open the Edit-control.", L"Error!", MB_OK);
						break;
					}
					if(iEditControlRes == TRUE) // Changes were made, update the listview so that it reflects the change
					{
						// Update the ListView item text
						ListView_SetItemText(hListView, lvItem.iItem, 0, ((std::vector<LPWSTR>*)(lvItem.lParam))->at(0));
						ListView_SetItemText(hListView, lvItem.iItem, 1, ((std::vector<LPWSTR>*)(lvItem.lParam))->at(1));
						ListView_SetItemText(hListView, lvItem.iItem, 2, ((std::vector<LPWSTR>*)(lvItem.lParam))->at(2));
						// Update the display
						ListView_SortItems(hListView, ListViewSortFunc, NULL);
						AutoAdjustListViewColumns(hListView);
						// Mark that there are unsaved changes
						bUnsavedChanges = TRUE;
					}
					break;
				}
				case IDC_MAIN_AUTOFIX:
				{
					if(vecResourceH.empty()) // Don't try to do this on an empty vector
					{
						break;
					}
					HWND hListView = GetDlgItem(hWnd, IDC_MAIN_VIEW);

					// Sort the vector and listview
					//std::sort(vecResourceH.begin(), vecResourceH.end(), VectorSort);
					ListView_SortItems(hListView, ListViewSortByTypeAndName, NULL);
					int iItem = ListView_GetNextItemIndex(hListView, -1, LVNI_BELOW);
					LVITEM lvItem;
					lvItem.mask = LVIF_PARAM;
					lvItem.iItem = iItem;
					ListView_GetItem(hListView, &lvItem);

					// Re-number everything using the numbering scheme provided on MSDN
					DWORD dwID = 0x64;
					LPWSTR wstrCurrentType = wstrAccel;
					//std::vector<std::vector<LPWSTR>*>::iterator viResourceH = vecResourceH.begin();
					while(iItem != -1)
					{
						while(((std::vector<LPWSTR>*)(lvItem.lParam))->at(0) == wstrCurrentType)
						{
							
							// Only change the ID if it really needs to be changed.
							if(wcstol(((std::vector<LPWSTR>*)(lvItem.lParam))->at(2), NULL, 10) != dwID)
							{
								ListView_SetItemText(hListView, lvItem.iItem, 2, NULL); // Set the display string to NULL to avoid problems when deleting the string
								delete [] ((std::vector<LPWSTR>*)(lvItem.lParam))->at(2); // Delete string
								DWORD dwStrLen = IDDigitCount(dwID) + 1; // Count the number of digits for the new string
								LPWSTR wstrNewID = new WCHAR[dwStrLen];
								StringCchPrintf(wstrNewID, dwStrLen, L"%u", dwID);
								((std::vector<LPWSTR>*)(lvItem.lParam))->at(2) = wstrNewID;
								ListView_SetItemText(hListView, lvItem.iItem, 2, ((std::vector<LPWSTR>*)(lvItem.lParam))->at(2));
							}
							dwID++;
							iItem = ListView_GetNextItem(hListView, lvItem.iItem, LVNI_BELOW);
							if(iItem == -1) break;
							lvItem.iItem = iItem;
							ListView_GetItem(hListView, &lvItem);
						}
						if(wstrCurrentType == wstrAccel)	{ wstrCurrentType = wstrIcon; continue; }
						if(wstrCurrentType == wstrIcon)		{ wstrCurrentType = wstrString; continue; }
						if(wstrCurrentType == wstrString)	{ wstrCurrentType = wstrDialog; continue; }
						if(wstrCurrentType == wstrDialog)	{ wstrCurrentType = wstrControl; dwID = 0x1000; continue; }
						if(wstrCurrentType == wstrControl)	{ wstrCurrentType = wstrMenu; dwID = 0x8000; continue; }
						break;
					}
					/*int iIndex = 0;
					for(viResourceH = vecResourceH.begin(); viResourceH != vecResourceH.end(); viResourceH++, iIndex++)
					{
						ListView_SetItemText(hListView, iIndex, 2, (*viResourceH)->at(2));
					}*/

					bUnsavedChanges = TRUE;
					break;
				}
				case IDC_MAIN_QUIT:
				{
					// Initiate shutdown
					SendMessage(hWnd, WM_CLOSE, NULL, NULL);
					break;
				}
				default: return FALSE;
			}
			break;
		}
		case WM_NOTIFY: // Listen to ListView events, due to the switch-case depth using break; is inconvenient, use return TRUE/FALSE instead.
		{
			LPNMLISTVIEW lpNMListView = (LPNMLISTVIEW)lParam;
			switch(lpNMListView->hdr.code)
			{
				case NM_DBLCLK:
				{
					if(lpNMListView->hdr.idFrom != IDC_MAIN_VIEW) return FALSE;
					SendMessage(hWnd, WM_COMMAND, IDC_MAIN_EDIT, 0); // Simulate a click of the Edit button.
					// Fall through to NM_CLICK, IDC_MAIN_EDIT checks the selected item before it calls the edit-window.
				}
				case NM_CLICK:
				{
					if(lpNMListView->hdr.idFrom != IDC_MAIN_VIEW) return FALSE;
					ListView_SetItemState(lpNMListView->hdr.hwndFrom, -1, 0, (UINT)-1);
					ListView_SetItemState(lpNMListView->hdr.hwndFrom, lpNMListView->iItem, LVIS_SELECTED, (UINT)-1);
					return TRUE;
				}
			}
			return FALSE;
		}
		case WM_CLOSE:
		{
			if(bUnsavedChanges)
			{
				int iRes = MessageBox(hWnd, L"There are unsaved changes to Resource.h, quit anyway?", L"Warning!", MB_YESNO);
				if(iRes == IDNO)
				{
					break;
				}
			}
			// Initiate shutdown
			DestroyWindow(hWnd);
			break;
		}
		case WM_DESTROY:
		{
			// Cleanup;
			ClearVecResourceH();
			// And send quit message:
			PostQuitMessage(1);
			break;
		}
		default: return FALSE;
	}

	return TRUE;
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	hSavedInstance = hInstance;

	HWND hwndMain = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDialogFunc);

	if(hwndMain == NULL)
	{
		return 0;
	}

	ShowWindow(hwndMain, nCmdShow);
	UpdateWindow(hwndMain);

	// Main message loop:
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}
